package com.lagou.edu.spring.core.defaults;

import com.lagou.edu.spring.core.BeanDefinition;
import com.lagou.edu.spring.core.BeanFactory;
import com.lagou.edu.spring.exceptions.MySpringException;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * bean的创建器
 * 功能包括：创建bean实例，属性注入
 *
 * @author 大犬
 * @date 2020/4/2
 */
public class BeanInstantiationFactory {

    private BeanFactory beanFactory;

    public BeanInstantiationFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }


    public Object instantiatesBean(BeanDefinition beanDefinition) {
        try {
            return doInstantiatesBean(beanDefinition);
        } catch (Exception e) {
            throw new MySpringException("instant bean failed", e);
        }
    }

    private Object doInstantiatesBean(BeanDefinition beanDefinition) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        Class beanType = beanDefinition.getBeanType();
        Object provideBean = beanDefinition.getProvideBean();
        if (provideBean != null) {
            return provideBean;
        }
        String parentBeanName = beanDefinition.getParentBeanName();
        Method beanCreateMethod = beanDefinition.getBeanCreateMethod();
        List<String> dependenceBeanNames = beanDefinition.getCreateMethodDependenceBeanNames();
        Object beanInstance = null;
        // 使用构建方法创建bean对象
        if (beanCreateMethod != null) {
            Object parentBean = beanFactory.getBean(parentBeanName);
            List<Object> dependencies;
            if (dependenceBeanNames != null && dependenceBeanNames.size() != 0) {
                // 有依赖参数的需要先获取依赖bean
                dependencies = dependenceBeanNames.stream()
                        .map(beanFactory::getBean)
                        .collect(Collectors.toList());
                try {
                    beanInstance = beanCreateMethod.invoke(parentBean, dependencies.toArray());
                } catch (Throwable e) {
                    throw new MySpringException("bean method invoke occur exception", e);
                }
            } else {
                System.out.println("方法创建对象：" + beanCreateMethod);
                // 直接调用
                try {
                    beanInstance = beanCreateMethod.invoke(parentBean);
                } catch (Throwable e) {
                    throw new MySpringException("bean method invoke occur exception", e);
                }
            }
        } else {
            if (beanType.isInterface()) {
                throw new MySpringException("cannot instant an interface bean:" + beanType);
            }
            // 使用bean类型进行无参构造
            beanInstance = beanType.newInstance();
        }
        if (beanInstance == null) {
            throw new MySpringException("create Bean failed");
        }
        // 返回实例化好的对象
        return beanInstance;
    }

    public void populate(Object bean, BeanDefinition beanDefinition) {
        Map<String, Field> propertyDependencies = beanDefinition.getPropertyDependencies();
        if (propertyDependencies != null) {
            propertyDependencies.forEach((name, field) -> {
                Object propertyBean = beanFactory.getBean(name);
                try {
                    field.set(bean, propertyBean);
                } catch (IllegalAccessException e) {
                    throw new MySpringException("try to set field value failed", e);
                }
            });
        }
    }
}
